#include "gtest/gtest.h"
#include "../BaseUtil/MyTest.h"
#include "PANEImp.h"
namespace PANEImp {


class PANEImageBoardElementTest: public testing::Test {
protected:
	virtual void SetUp() {
		doc = new PANEDocument();
		board = doc->createPANEImageBoardElement();

    	vd = new VoodooLoader();
		model = new ModelReconstructor();

		model->importVoodoo( vd );
		board->setModelReconstructor( model );
	}

	virtual void TearDown() {
		delete model;
    	delete vd;
		delete doc;
	}

    static bool isSameImage( IplImage * img1, IplImage * img2 ) {
    	if( img1->height != img2->height ) return false;
    	if( img1->width != img2->width ) return false;

    	for( int i = 0; i < img1->height; ++i )
    		for( int j = 0; j < img1->width; ++j ) {
    			const CvScalar dot1 = cvGet2D( img1, i, j );
    			const CvScalar dot2 = cvGet2D( img2, i, j );
    			if( dot1.val[0] != dot2.val[0] ) return false;
    			if( dot1.val[1] != dot2.val[1] ) return false;
    			if( dot1.val[2] != dot2.val[2] ) return false;
    		}
    	return true;
    }

	static MouseEvent * createMouseClickEvent( PANEImageBoardElement * board, int x, int y ) {
		MouseEvent * evt = dynamic_cast< MouseEvent * >( board->createEvent( MouseEventImp::EVENT_CLICK ) );
		evt->initMouseEvent(
				MouseEventImp::EVENT_CLICK, true, true, NULL, 0,
				0, 0, board->getLeftOffset() + x, board->getTopOffset() + y,
				false, false, false, false,
				MouseEvent::LEFT_MOUSE_BUTTON, NULL);
		return evt;
	}

	static MouseEvent * createMouseMoveEvent( PANEImageBoardElement * board, int x, int y ) {
		MouseEvent * evt = dynamic_cast< MouseEvent * >( board->createEvent( MouseEventImp::EVENT_MOUSE_MOVE ) );
		evt->initMouseEvent(
				MouseEventImp::EVENT_MOUSE_MOVE, true, true, NULL, 0,
				0, 0, board->getLeftOffset() + x, board->getTopOffset() + y,
				false, false, false, false,
				MouseEvent::LEFT_MOUSE_BUTTON, NULL);
		return evt;
	}

	static MouseEvent * createMouseMoveWithCtrlEvent( PANEImageBoardElement * board, int x, int y ) {
		MouseEvent * evt = dynamic_cast< MouseEvent * >( board->createEvent( MouseEventImp::EVENT_MOUSE_MOVE ) );
		evt->initMouseEvent(
				MouseEventImp::EVENT_MOUSE_MOVE, true, true, NULL, 0,
				0, 0, board->getLeftOffset() + x, board->getTopOffset() + y,
				true, false, false, false,
				MouseEvent::LEFT_MOUSE_BUTTON, NULL);
		return evt;
	}

	PANEDocument * doc;
	PANEImageBoardElement * board;
    VoodooLoader * vd;
	ModelReconstructor * model;
};

TEST_F(PANEImageBoardElementTest, LoadUpImageFiles) {
	board->loadImageFiles( "../VoodooData/car", 0, 10, ".jpeg", 1);
	ASSERT_EQ( 11ul, board->cvImages_.size() );

//	board->loadImageFiles( "../VoodooData/car", 0, 421, ".jpeg");
//	ASSERT_EQ( 422ul, board->cvImages_.size() );
}

TEST_F(PANEImageBoardElementTest, RenderAFrame) {
	board->loadImageFiles( "../VoodooData/car", 0, 10, ".jpeg", 1);
	ASSERT_EQ( 11ul, board->cvImages_.size() );

	board->setCurrentFrameNumber(3);
	ASSERT_EQ( 640ul, board->getWidth() );
	ASSERT_EQ( 480ul, board->getHeight() );

	IplImage * desiredImg = cvLoadImage( "../VoodooData/car003.jpeg" );
	ASSERT_TRUE( desiredImg );
	ASSERT_TRUE( *(desiredImg->imageData) );

	IplImage * output = cvCreateImage( cvSize(board->getWidth(), board->getHeight()),
			desiredImg->depth, desiredImg->nChannels );
	ASSERT_TRUE( output );
	board->render( output );
    ASSERT_TRUE( isSameImage( output, desiredImg) );
	cvReleaseImage( &output );

	cvReleaseImage( &desiredImg );
}

TEST_F(PANEImageBoardElementTest, LoadupVoodData) {
	board->loadImageFiles( "../VoodooData/car", 0, 10, ".jpeg", 1);
	ASSERT_TRUE( vd->loadCameraData("../VoodooData/car.txt") );
	ASSERT_TRUE( vd->loadFeaturePoints( "../VoodooData/car", 0, 10, ".pnt", 1) );
	ASSERT_EQ( model, board->model_ );
}

TEST_F(PANEImageBoardElementTest, AddinPoints) {
	board->setCurrentFrameNumber(3);
	board->setInteractionMethod( PANEImageBoardElement::DrawPoint );
	board->dispatchEvent( createMouseClickEvent( board, 30, 30 ) );
	board->dispatchEvent( createMouseClickEvent( board, 30, 30 ) );
	board->dispatchEvent( createMouseClickEvent( board, 30, 50 ) );
	ASSERT_EQ( 2ul, model->sizeFeaturePoints() );

	board->setCurrentFrameNumber(4);
	board->dispatchEvent( createMouseClickEvent( board, 30, 30 ) );
	ASSERT_EQ( 3ul, model->sizeFeaturePoints() );
}

TEST_F(PANEImageBoardElementTest, DrawPoints) {
	board->loadImageFiles( "../VoodooData/car", 0, 4, ".jpeg", 1);

	board->setCurrentFrameNumber(3);
	board->setInteractionMethod( PANEImageBoardElement::DrawPoint );
	board->dispatchEvent( createMouseClickEvent( board, 30, 30 ) );

	IplImage * originalImg = cvLoadImage( "../VoodooData/car003.jpeg" );
	ASSERT_TRUE( originalImg );

	IplImage * output = cvCreateImage( cvSize(board->getWidth(), board->getHeight()),
			originalImg->depth, originalImg->nChannels );
	ASSERT_TRUE( output );

	board->render( output );
	ASSERT_FALSE( isSameImage( output, originalImg) );

	cvReleaseImage( &output );
	cvReleaseImage( &originalImg );
}

TEST_F(PANEImageBoardElementTest, DrawVoodooFeaturePoints) {
	vd->loadCameraData("../VoodooData/car.txt");
	vd->loadFeaturePoints( "../VoodooData/car", 0, 4, ".pnt", 1);
	ASSERT_TRUE( vd->getNumberOfFeaturePoints() > 0 );
	ASSERT_TRUE( model->getVoodooFeaturePointsOnOneFrame( 3 ).size() > 0 );
	board->loadImageFiles( "../VoodooData/car", 0, 4, ".jpeg", 1);
	board->setCurrentFrameNumber(3);

	IplImage * originalImg = cvLoadImage( "../VoodooData/car003.jpeg" );
	ASSERT_TRUE( originalImg );

	IplImage * output = cvCreateImage( cvSize(board->getWidth(), board->getHeight()),
			originalImg->depth, originalImg->nChannels );
	ASSERT_TRUE( output );

	board->render( output );
	ASSERT_FALSE( isSameImage( output, originalImg) );

	cvReleaseImage( &output );
	cvReleaseImage( &originalImg );
}

TEST_F(PANEImageBoardElementTest, DrawPotentialPoints) {
	vd->loadCameraData("../VoodooData/car.txt");
	vd->loadFeaturePoints( "../VoodooData/car", 0, 4, ".pnt", 1);
	board->loadImageFiles( "../VoodooData/car", 0, 4, ".jpeg", 1);

	IplImage * originalImg = cvCreateImage( cvSize(board->getWidth(), board->getHeight()),
			IPL_DEPTH_8U, 3 );
	ASSERT_TRUE( originalImg );

	board->setCurrentFrameNumber(3);
	board->render( originalImg );

	board->setCurrentFrameNumber(2);
	board->setInteractionMethod( PANEImageBoardElement::DrawPoint );
	board->dispatchEvent( createMouseClickEvent( board, 30, 30 ) );
	board->dispatchEvent( createMouseClickEvent( board, 40, 40 ) );
	board->setCurrentFrameNumber(3);

	IplImage * output = cvCreateImage( cvSize(board->getWidth(), board->getHeight()),
			originalImg->depth, originalImg->nChannels );
	ASSERT_TRUE( output );

	board->render( output );
	ASSERT_FALSE( isSameImage( originalImg, output ) );

	cvReleaseImage( &output );
	cvReleaseImage( &originalImg );
}

TEST_F(PANEImageBoardElementTest, MovePoints) {
	vd->loadCameraData("../VoodooData/car.txt");
	vd->loadFeaturePoints( "../VoodooData/car", 0, 4, ".pnt", 1);
	board->loadImageFiles( "../VoodooData/car", 0, 4, ".jpeg", 1);

	board->setCurrentFrameNumber(2);
	board->setInteractionMethod( PANEImageBoardElement::DrawPoint );
	board->dispatchEvent( createMouseClickEvent( board, 30, 30 ) );
	board->dispatchEvent( createMouseClickEvent( board, 40, 40 ) );
	board->setCurrentFrameNumber(3);

	IplImage * originalImg = cvCreateImage( cvSize(board->getWidth(), board->getHeight()),
			IPL_DEPTH_8U, 3 );
	ASSERT_TRUE( originalImg );

	board->render( originalImg );

	board->setInteractionMethod( PANEImageBoardElement::Move );
	board->dispatchEvent( createMouseMoveWithCtrlEvent( board, 30, 30 ) );
	board->dispatchEvent( createMouseMoveWithCtrlEvent( board, 50, 50 ) );
	board->dispatchEvent( createMouseMoveEvent( board, 51, 52 ) );

	IplImage * output = cvCreateImage( cvSize(board->getWidth(), board->getHeight()),
			originalImg->depth, originalImg->nChannels );
	ASSERT_TRUE( output );

	board->render( output );
	ASSERT_FALSE( isSameImage( originalImg, output ) );

	cvReleaseImage( &output );
	cvReleaseImage( &originalImg );
}

TEST_F(PANEImageBoardElementTest, DrawLines) {
//	const Point2D point1(10, 10);
//	const Point2D point2(50, 30);
//	const Point2D point3(100, 30);
//
//	tool->selectTool(DrawingToolPaneCtrl::DrawPoint);
//	board->dispatchEvent( createEventClick( point1 ) );
//	board->dispatchEvent( createEventClick( point2 ) );
//	board->dispatchEvent( createEventClick( point3 ) );
//
//	tool->selectTool(DrawingToolPaneCtrl::DrawLine);
//	board->dispatchEvent( createEventClick( point1 ) );
//	ASSERT_EQ((size_t) 0, model->sizeFeatureLines());
//
//	board->dispatchEvent( createEventClick( point2 ) );
//	ASSERT_EQ((size_t) 1, model->sizeFeatureLines());
//
//	board->dispatchEvent( createEventClick( point3 ) );
//	ASSERT_EQ((size_t) 2, model->sizeFeatureLines());
//
//	board->dispatchEvent( createEventClick( point3 ) );
//	ASSERT_EQ((size_t) 2, model->sizeFeatureLines()) << "Redundant points are addable";
//
//	board->dispatchEvent( createEventClick( point1 ) );
//	ASSERT_EQ((size_t) 3, model->sizeFeatureLines());
}

TEST_F(PANEImageBoardElementTest, DrawLinesByDragging) {
//	FAIL();
}

TEST_F(PANEImageBoardElementTest, RemoveLines) {
//	const Point2D point1(10, 10);
//	const Point2D point2(50, 30);
//	const Point2D point3(150, 30);
//
//	tool->selectTool(DrawingToolPaneCtrl::DrawPoint);
//	board->dispatchEvent( createEventClick( point1 ) );
//	board->dispatchEvent( createEventClick( point2 ) );
//	board->dispatchEvent( createEventClick( point3 ) );
//
//	tool->selectTool(DrawingToolPaneCtrl::DrawLine);
//	board->dispatchEvent( createEventClick( point1 ) );
//	board->dispatchEvent( createEventClick( point2 ) );
//	board->dispatchEvent( createEventClick( point3 ) );
//	board->dispatchEvent( createEventClick( point1 ) );
//	ASSERT_EQ((size_t) 3, model->sizeFeatureLines());
//
//	tool->selectTool(DrawingToolPaneCtrl::RemoveLine);
//	board->dispatchEvent( createEventClick( (point2 + point3) /2 ) );
//	ASSERT_EQ((size_t) 2, model->sizeFeatureLines());
//
//	board->dispatchEvent( createEventClick( (point2 + point3) / 2 ) );
//	ASSERT_EQ((size_t) 2, model->sizeFeatureLines()) << "A line that is not correctly pointed is removed.";
//
//	board->dispatchEvent( createEventClick( (point1 + point2) * (4.f / 6.f) ) );
//	ASSERT_EQ((size_t) 1, model->sizeFeatureLines());
//
//	board->dispatchEvent( createEventClick( (point3 + point1) / 2.f ) );
//	ASSERT_EQ((size_t) 0, model->sizeFeatureLines());
}

TEST_F(PANEImageBoardElementTest, RemovePoints) {
//	const Point2D point1(10, 10);
//	const Point2D point2(50, 30);
//	const Point2D point3(150, 30);
//
//	tool->selectTool(DrawingToolPaneCtrl::DrawPoint);
//	board->dispatchEvent( createEventClick( point1 ) );
//	board->dispatchEvent( createEventClick( point2 ) );
//	board->dispatchEvent( createEventClick( point3 ) );
//
//	tool->selectTool(DrawingToolPaneCtrl::DrawLine);
//	board->dispatchEvent( createEventClick( point1 ) );
//	board->dispatchEvent( createEventClick( point2 ) );
//	board->dispatchEvent( createEventClick( point3 ) );
//	board->dispatchEvent( createEventClick( point1 ) );
//
//	tool->selectTool(DrawingToolPaneCtrl::RemovePoint);
//	board->dispatchEvent( createEventClick( point2 ) );
//	ASSERT_EQ((size_t) 2, model->sizeFeaturePoints());
//	ASSERT_EQ((size_t) 1, model->sizeFeatureLines());
//
//	board->dispatchEvent( createEventClick( point1 ) );
//	ASSERT_EQ((size_t) 1, model->sizeFeaturePoints());
//
//	board->dispatchEvent( createEventClick( point3 ) );
//	ASSERT_EQ((size_t) 0, model->sizeFeaturePoints());
}

TEST_F(PANEImageBoardElementTest, PointClickTolerance) {
}

TEST_F(PANEImageBoardElementTest, LineClickTolerance) {
}

}

